package com.bwton.msx.ccb.ui;

import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_DETECTING;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_EYE_CLOSE;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_HEADPOSE_ANGLE_FAIL;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_MOUTH_OPEN;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_MULTIPLE_FACE;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_NOT_FOUND;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_OCCLUSION_BROW;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_OCCLUSION_CHEEK;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_OCCLUSION_EYE;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_OCCLUSION_MOUTH;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_OCCLUSION_NOSE;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_OVER_DARK;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_OVER_GLARE;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_POSITION_BACKWARD;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_POSITION_DOWN;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_POSITION_FORWARD;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_POSITION_LEFT;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_POSITION_RIGHT;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_POSITION_UP;
import static com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus.ST_PHASE_STATUS_TOO_BLUR;

import android.content.Context;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.bwton.msx.ccb.R;
import com.bwton.msx.ccb.utils.FileUtil;
import com.sensetime.ssidmobile.sdk.liveness.OnLivenessListener;
import com.sensetime.ssidmobile.sdk.liveness.OnLogsListener;
import com.sensetime.ssidmobile.sdk.liveness.STLiveness;
import com.sensetime.ssidmobile.sdk.liveness.constants.STFaceStatus;
import com.sensetime.ssidmobile.sdk.liveness.constants.STResultCode;
import com.sensetime.ssidmobile.sdk.liveness.model.Location;
import com.sensetime.ssidmobile.sdk.liveness.model.STResultImage;
import com.sensetime.ssidmobile.sdk.liveness.model.STSignedData;
import com.sensetime.ssidmobile.sdk.liveness.model.config.DetectorConfig;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;

public abstract class LivenessActivity extends AbstractCameraActivity implements OnLivenessListener, OnLogsListener {

    protected STLiveness mSTLiveness;

    protected Rect mTargetRect;

    boolean isInput;

    private int mFaceCount = 0;

    // 最大重试次数
    private static final int RESTART_LIMIT = 3;

    // 是否检测到人脸
    boolean isFoundFace = false;

    // 当前重试次数
    protected int restartTimes = 0;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        showProgress();
        new AsyncTask<Context, Void, DetectorConfig>() {

            @Override
            protected DetectorConfig doInBackground(Context... contexts) {
                // 配置Debug日志监听（正式版建议删除）
                STLiveness.setLogger(LivenessActivity.this);
                return createDetectorConfig();
            }

            @Override
            protected void onPostExecute(DetectorConfig detectorConfig) {
                init(detectorConfig);
                dismissProgress();
            }

        }.execute(this);
    }

    protected abstract DetectorConfig createDetectorConfig();

    //初始化扫描器
    protected abstract void init(DetectorConfig detectorConfig);

    @Override
    public void onUpdateQualityStatus(@STFaceStatus int faceState) {
        if (faceState == ST_PHASE_STATUS_OCCLUSION_BROW) {
            changeHint(R.string.error_status_occlusion_brow);
        } else if (faceState == ST_PHASE_STATUS_OCCLUSION_EYE) {
            changeHint(R.string.error_status_occlusion_eye);
        } else if (faceState == ST_PHASE_STATUS_EYE_CLOSE) {
            changeHint(R.string.error_status_close_eye);
        } else if (faceState == ST_PHASE_STATUS_OCCLUSION_NOSE) {
            changeHint(R.string.error_status_occlusion_nose);
        } else if (faceState == ST_PHASE_STATUS_OCCLUSION_MOUTH) {
            changeHint(R.string.error_status_occlusion_mouth);
        } else if (faceState == ST_PHASE_STATUS_OCCLUSION_CHEEK) {
            changeHint(R.string.error_status_occlusion_cheek);
        } else if (faceState == ST_PHASE_STATUS_HEADPOSE_ANGLE_FAIL) {
            changeHint(R.string.error_status_angle);
        } else if (faceState == ST_PHASE_STATUS_TOO_BLUR) {
            changeHint(R.string.error_status_blur);
        } else if (faceState == ST_PHASE_STATUS_OVER_DARK) {
            changeHint(getString(R.string.error_status_too_dark));
        } else if (faceState == ST_PHASE_STATUS_OVER_GLARE) {
            changeHint(R.string.error_status_overexposure);
        } else if (faceState == ST_PHASE_STATUS_POSITION_UP
                || faceState == ST_PHASE_STATUS_POSITION_DOWN
                || faceState == ST_PHASE_STATUS_POSITION_LEFT
                || faceState == ST_PHASE_STATUS_POSITION_RIGHT
                || faceState == ST_PHASE_STATUS_NOT_FOUND) {
            if (isFoundFace) {
                restartDetector();
            } else {
                changeHint(R.string.error_status_out_bound);
            }
        } else if (faceState == ST_PHASE_STATUS_POSITION_FORWARD) {
            changeHint(R.string.error_status_too_close);
        } else if (faceState == ST_PHASE_STATUS_POSITION_BACKWARD) {
            changeHint(R.string.error_status_too_far);
        } else if (faceState == ST_PHASE_STATUS_MULTIPLE_FACE) {
            changeHint(getString(R.string.error_face_count_cheek) + "\n当前用户数为:" + mFaceCount);
        } else if (faceState == ST_PHASE_STATUS_MOUTH_OPEN) {
            changeHint(R.string.error_status_mouth_open);
        } else if (faceState == ST_PHASE_STATUS_DETECTING) {
            isFoundFace = true;
            showDetectingHint();
        }
    }

    @Override
    public void onUpdateFaceRect(Location[] locations) {

    }

    @Override
    public void onUpdateFaceCount(int faceCount) {
        mFaceCount = faceCount;
    }

    @Override
    public void onLogs(int level, String message) {
        Log.println(level, "Liveness日志", message);
    }


    protected void restartDetector() {
        if (!isFoundFace) {
            return;
        }
        isFoundFace = false;
        if (restartTimes < RESTART_LIMIT) {
            restartTimes++;
            Toast.makeText(this, String.format("第%d次重新启动检测", restartTimes), Toast.LENGTH_SHORT).show();
            changeHint(R.string.error_status_out_bound);
            restartLiveness();
        } else if (restartTimes >= RESTART_LIMIT) {
            restartTimes = 0;
            finish();
            final ResultActivity.Result result =
                    new ResultActivity.Result();
            result.resultCode = STResultCode.FACE_LOST;
            startResultActivity(result);
        }
    }

    protected abstract void showDetectingHint();

    protected abstract void startResultActivity(ResultActivity.Result result);

    protected abstract void restartLiveness();


    public ResultActivity.Result saveResult(STResultImage originalResultImage, STResultImage cropResultImage, STResultImage[] stageResultImages, STSignedData signedData) {
        final ResultActivity.Result result = new ResultActivity.Result();
        if (originalResultImage == null) {
            return result;
        }

        File dir = FileUtil.mkDirs(String.format("%s/result/%s", getCacheDir().getAbsolutePath(), "cache"),
                "signImage");

        //原图和裁剪图
        if (originalResultImage != null) {
            String resultFilePath =
                    dir.getAbsolutePath() + File.separator + "livenessImage0.png";
            FileUtil.saveBytes(originalResultImage.jpeg, resultFilePath);
            result.bitmapFilePaths.add(resultFilePath);
            writeSigns(dir.getAbsolutePath(), "livenessSignature0",
                    originalResultImage.sign);
        }
        //原图和裁剪图
        if ((cropResultImage != null)
                && (cropResultImage.jpeg != null)) {
            String cropFilePath =
                    dir.getAbsolutePath() + File.separator + "liveness.png";
            FileUtil.saveBytes(cropResultImage.jpeg, cropFilePath);
            result.bitmapFilePaths.add(cropFilePath);
            writeSigns(dir.getAbsolutePath(), "livenessSignature1",
                    cropResultImage.sign);
        }
        // 加签数据
        if (signedData != null
                && (signedData.data != null)) {
            String signedFilePath =
                    dir.getAbsolutePath() + File.separator + "signedDataImage.json";
            FileUtil.saveBytes(signedData.data.getBytes(StandardCharsets.UTF_8), signedFilePath);
            writeSigns(dir.getAbsolutePath(), "signedDataSignature",
                    signedData.sign);
        }

        if (stageResultImages == null
                || stageResultImages.length == 0) {
            return result;
        }
        //动作图片和签名
        for (int i = 0; i < stageResultImages.length; i++) {
            //结果图
            String otherFilePath =
                    dir.getAbsolutePath() + File.separator + "livenessImage" + (i + 2) + ".png";
            FileUtil.saveBytes(stageResultImages[i].jpeg, otherFilePath);
            result.bitmapFilePaths.add(otherFilePath);

            //签名
            writeSigns(dir.getAbsolutePath(), "livenessSignature" + (i + 2),
                    stageResultImages[i].sign);
        }
        return result;
    }


    private void writeSigns(String dir, String signFileName, String sign) {
        if (TextUtils.isEmpty(sign)) {
            return;
        }
        try {
            final File signFile = new File(dir, signFileName + ".txt");
            final RandomAccessFile raf = new RandomAccessFile(signFile, "rwd");
            raf.seek(raf.length());
            raf.write(sign.getBytes("utf-8"));
            raf.close();
        } catch (IOException e) {
        }
    }

}
